package com.one.pass.service;

import com.one.pass.config.JacksonUtil;
import com.one.pass.config.LockType;
import com.one.pass.config.RedisLock;
import com.one.pass.config.TryRun;
import com.one.pass.controller.req.BatchPayReq;
import com.one.pass.controller.req.UserTradeReq;
import com.one.pass.controller.resp.BaseResponse;
import com.one.pass.dal.AccountMoney;
import com.one.pass.dal.UtilMapper;
import com.one.pass.fegin.PayFegin;
import com.one.pass.fegin.PayReq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static com.one.pass.util.MapUtils.asMap;
/*
MoneyService类提供了批量充值、用户间转账、查询用户账户金额等功能。
通过使用Redis锁和异步处理，确保操作的幂等性和高并发处理能力。
使用@RedisLock和@TryRun注解来简化锁机制和重试逻辑的实现。
*/

@Service
@Slf4j
public class MoneyService {

    @Autowired
    private RetryTemplate retryTemplate;

    @Autowired
    @Lazy  // 延迟注入，避免循环依赖
    private MoneyService moneyService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private PayFegin payFegin;

    @Autowired
    private UtilMapper utilMapper;

    @Autowired
    private HttpServletRequest httpServletRequest;

    // 批量充值
    @RedisLock(prefix = "批量充值", lockKey = "'batchPay::'+batchPayReq.batchPayId",
            lockType = LockType.NO_WAIT, error = "正在处理中")
    public Map<String, Object> batchPay(BatchPayReq batchPayReq) {
        if (batchPayReq.getUids() == null || batchPayReq.getUids().isEmpty()) {
            return success();
        }

        String s = redisService.getString("batchId::" + batchPayReq.getBatchPayId());
        // 判断这个批次是否处理过了
        if (!StringUtils.isEmpty(s)) {
            return success();
        }

        redisService.saveString("batchId::" + batchPayReq.getBatchPayId(), "1", 24 * 60 * 60 * 1000);
        moneyService.start(batchPayReq);
        return success();
    }

    @Async
    public void start(BatchPayReq batchPayReq) {
        // 开始线程去处理批量充值，体现性能
        batchPayReq.getUids().parallelStream()
                .forEach(uid -> dealBatchItem(new PayReq(UUID.randomUUID().toString(), uid, new BigDecimal("10000"))));
        // 批量充值回调
//        payFegin.batchPayFinish(asMap("batchPayId", batchPayReq.getBatchPayId()));

        BaseResponse response = payFegin.batchPayFinish(asMap("batchPayId", batchPayReq.getBatchPayId()));
        log.info("批量充值回调结果: {}", response);
    }

    // 处理单个账户
    private void dealBatchItem(PayReq payReq) {
        log.info("开始处理:{}", payReq.getUid());
        BigDecimal totalAmount = BigDecimal.ZERO;
        while (true) {
            try {
                // 扣除资金
                BaseResponse baseResponse = payFegin.thirdPay(payReq);
//            try {
//                // 使用 RetryTemplate 执行扣款操作
//                BaseResponse baseResponse = retryTemplate.execute(new RetryCallback<BaseResponse, Exception>() {
//                    @Override
//                    public BaseResponse doWithRetry(RetryContext context) throws Exception {
//                        // 扣除资金
//                        return payFegin.thirdPay(payReq);
//                    }
//                });
                log.info("参数:{}, 返回值:{}", JacksonUtil.obj2Json(payReq), JacksonUtil.obj2Json(baseResponse));
                if (baseResponse.getCode() == 200) {
                    // 交易成功
                    payReq.setTransactionId(UUID.randomUUID().toString());
                    totalAmount = totalAmount.add(payReq.getAmount());
                } else if (baseResponse.getCode() == 501) {
                    // 余额不足
                    if (payReq.getAmount().compareTo(new BigDecimal("0.01")) == 0) {
                        break;
                    }
                    // 余额不够，把扣款金额减半
                    payReq.setAmount(payReq.getAmount().divide(new BigDecimal("2.0"), 2, RoundingMode.HALF_UP));
                    if (payReq.getAmount().compareTo(new BigDecimal("0.03")) > 0) {
                        payReq.setAmount(payReq.getAmount().add(new BigDecimal("0.01")));
                    }
                    payReq.setTransactionId(UUID.randomUUID().toString());
                }
            } catch (Exception e) {
                log.error("处理过程中发生异常", e);
            }
        }
        log.info("获取金额:{}", totalAmount);
        // 结束后，添加金额
        moneyService.addAccountMoney(new AccountMoney(payReq.getUid(), totalAmount));
    }

    @RedisLock(lockKey = "'userTrade::'+userTradeReq.getUuid()", error = "重复请求", lockType = LockType.NO_WAIT)
    public Map<String, Object> userTrade(UserTradeReq userTradeReq) {
        String reId = "tradeNo" + getReId();
        String i = redisService.getString(reId);
        if (!StringUtils.isEmpty(i)) {
            return success();
        }
        redisService.saveString(reId, "1", 24 * 60 * 60 * 1000);

        List<AccountMoney> accountMoneyList = utilMapper.select(Collections.singletonList(userTradeReq.getTargetUid()));
        if (accountMoneyList.isEmpty()) {
            return error("目标账户不存在");
        }

        BigDecimal amountTemp = userTradeReq.getAmount();
        BigDecimal minTemp = new BigDecimal("0.01");
        BigDecimal maxTemp = new BigDecimal("10000.00");
        if(amountTemp.compareTo(minTemp) < 0){
            return error("金额过小");
        }
        if(amountTemp.compareTo(maxTemp) > 0){
            return error("金额过大");
        }

        // 扣减来源账户的钱
        if (!moneyService.addAccountMoney(new AccountMoney(userTradeReq.getSourceUid(),
                userTradeReq.getAmount().multiply(new BigDecimal("-1"))))) {
            return error("余额不足");
        }
        // 给目标账户加钱
        moneyService.addAccountMoneyTry(new AccountMoney(userTradeReq.getTargetUid(), userTradeReq.getAmount()));
        return success();
    }

    @TryRun("操作账户一定要成功")
    public void addAccountMoneyTry(AccountMoney accountOp) {
        moneyService.addAccountMoney(accountOp);
    }

    @RedisLock(lockKey = "'addAccountMoney'+accountOp.getUid()", error = "操作账户异常",
            lockType = LockType.WAIT_THROW_EXCEPTION)
    public boolean addAccountMoney(AccountMoney accountOp) {
        List<AccountMoney> accountMoneyList = utilMapper.select(Collections.singletonList(accountOp.getUid()));
        AccountMoney accountMoney = accountMoneyList.isEmpty() ? new AccountMoney(accountOp.getUid(), BigDecimal.ZERO) : accountMoneyList.get(0);
        if (accountOp.getAmount().compareTo(BigDecimal.ZERO) < 0 && accountMoney.getAmount().compareTo(accountOp.getAmount().abs()) < 0) {
            // 余额不够
            return false;
        }
        accountOp.setAmount(accountMoney.getAmount().add(accountOp.getAmount()));
        utilMapper.save(accountOp);
        return true;
    }

    // 成功的响应
    private Map<String, Object> success() {
        return asMap("code", 200, "msg", "ok", "requestId", getReId());
    }

    // 错误的响应
    private Map<String, Object> error(String error) {
        return asMap("code", 100, "msg", error, "requestId", getReId());
    }

    // 获取请求ID
    public String getReId() {
        return httpServletRequest.getHeader("X-KSY-REQUEST-ID");
    }

    // 查询用户账户金额
    public Object queryUserAmount(List<Long> ids) {
        return asMap("code", 200, "msg", "ok", "requestId", getReId(), "data", utilMapper.select(ids));
    }
}
